package com.company.exp2.station;

import com.company.exp2.Interface_.FrameListener;
import com.company.exp2.Interface_.ChannelListener;
import com.company.exp2.frame.Frame;

/**
 * 存储待发送帧的站点，拥有自己的编号
 * 注意，station 的编号并不是由自己定的，而是在 Context 中注册后，由 Context 设置，station 并不需要管理自己的编号
 * 所有 Station 的实现类都不需要管理编号
 * 请不要对分配的 编号 进行任何额外的处理，以防止篡改导致系统紊乱
 * 可以发送帧和接收帧
 * 接收帧后应该返回 ACK帧 给源站
 * station 应该含有自己的待传输帧缓存 Buffer
 */
public abstract class Station implements ChannelListener, FrameListener {

    private int no;

    /**
     * 站点添加一个帧
     * @param frame 需要添加到当前站点的帧
     */
    public abstract void insertFrame(Frame frame, long timestamp);

    /**
     * 触发条件：
     *      当信道信道接收到 CHANNEL_TRANSMITTABLE 事件后，
     *      会调用所有其内部的站点的 onChannelTransmittable
     *
     * 如果自身有需要传递的 Frame 的话，开启二元指数回退
     *
     * 必须跟踪自身的 将要发生的startTransmissionEvent ，直到其执行或被下一个事件顶替
     * 跟踪的目的是为了模拟当其他站点发送 Frame 时，回退倒计时冻结
     *
     * 当发送成功后，应该设置 keepingAvoiding 为 false, 并且重置 avoidFrequency
     */
    @Override
    public abstract void onChannelTransmittable(long timestamp);

    /**
     * 如果接收到 Channel 拒绝的通知时，应当将自己的当前 avoid 的事件发生时间戳延后
     * 延后大小为当前发送帧所需时间
     */
    @Override
    public abstract void onChannelForbidden(long timestamp);

    /**
     *  由 FrameStartTransmittingEvent 事件调用
     *
     *  执行这个方法说明站点已经经过了回避碰撞
     *  这里将会直接发送 Frame 到信道之中
     *  此时站点将不再处于回避状态
     *
     *  值得注意的是，这个时间点并不能重置二元指数回退的次数
     *  因为还可能因为传输失败而再次回避
     */
    @Override
    public abstract void onFrameStartTransmit(long timestamp);

    /**
     * 当帧传输成功时调用
     * 释放掉已传输成功的帧的缓存
     *
     * 注意，二进制指数回退的系数不能清空
     */
    @Override
    public abstract void onFrameTransmissionSucceed(long timestamp);

    /**
     * 当帧传输超时时调用
     * 由 FRAME_TIMEOUT 触发
     * 这个方法将导致站点开始回避，并准备下一次发送事件
     */
    @Override
    public abstract void onFrameTransmissionFailed(long timestamp);

    /**
     * 获取编号
     */
    public int getNo() {
        return no;
    }

    /**
     * 设置编号
     */
    public void setNo(int no) {
        this.no = no;
    }

    /**
     * 生成模型的模拟过程的数据
     * 包括队列的分布情况以及等待时间分布
     */
    public abstract void log();

    /**
     * 当传输成功时调用，清除传输完成的帧的缓存
     */
    protected abstract void dropFirstFrame();

}
